/*
 * Created on Mar 16, 2009
 */
package com.mgmaps.utils;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import ro.letsdoitromania.WelcomePaintArgs;
import com.mgmaps.tools.StringTools;

/**
 * Generic welcome screen implementation.
 *
 * @author CristiS
 * @version 1.0
 */
public class WelcomeScreen {
  private String[] options;
  private int current;
  private Image[] icons;
  private WelcomePaintArgs paintArgs;
  private final int numOptions;
  private int optionsPerRow;
  private int optionRows;
  private int w;
  private int h;
  
  private static final int SPACING_SELECTBORDERS = 2;
  private static final int SPACING_IMGTEXT = 5;
  
  public WelcomeScreen(final String[] options, final Image[] icons, final WelcomePaintArgs paintArgs, int w, int h) {
    this(options, icons, paintArgs);
    
    // set size
    setSize(w, h);
  }
  
  public WelcomeScreen(final String[] options, final Image[] icons, final WelcomePaintArgs paintArgs) {
    this.options = options;
    this.icons = icons;
    numOptions = options.length;
    current = 0;
    optionRows = 1;
    optionsPerRow = numOptions;
    this.paintArgs = paintArgs;
  }
  
  public void setSize(int newW, int newH) {
    // ignore setSize when width or height are zero or unchanged
    if (!needsSize(newW, newH))
      return;
    
    w = newW;
    h = newH;
    
    // compute number of options per row
    if (numOptions == 1)
      optionsPerRow = 1;
    else if (numOptions <= 8 && h > w || numOptions <= 4 && w >= h)
      optionsPerRow = 2;
    else if (h > w || numOptions <= 9 && w >= h)
      optionsPerRow = 3;
    else
      optionsPerRow = 4;
    
    optionRows = (numOptions-1)/optionsPerRow + 1;
  }
  
  public void setIcons(Image[] icons) {
    this.icons = icons;
  }
  
  /**
   * Replace text for an option
   * @param num option index
   * @param option option text
   */
  public void replaceOption(int num, String option) {
    options[num] = option;
  }
  
  /**
   * Replace text for all options.
   * @param newOptions new option text
   */
  public void setOptions(String[] newOptions) {
    options = newOptions;
  }
  
  /**
   * Set the painting arguments
   * @param paintArgs new painting arguments
   */
  public void setPaintArgs(WelcomePaintArgs paintArgs) {
    this.paintArgs = paintArgs;
  }
  
  /**
   * Paint the welcome screen.
   * @param g Graphics object
   * @param x x coord
   * @param y y coord
   */
  public void paint(Graphics g, int x, int y) {
    if (w == 0 || h == 0)
      return;
    
    final int lastRowOptions = optionsPerRow-optionsPerRow*optionRows+numOptions;
    final Font font = paintArgs.getFont();
    g.setClip(x, y, w, h);
    
    // paint background
    g.setColor(paintArgs.getBackgroundColor());
    g.fillRect(x, y, w, h);
    
    // draw lines between options
    g.setColor(paintArgs.getLineColor());
    
    // horizontal
    for (int i=1; i<optionRows; i++)
      g.drawLine(x, y+i*h/optionRows, x+w-1, y+i*h/optionRows);
    // vertical
    final int lastLine = (optionRows-1)*h/optionRows;
    for (int i=1; i<optionsPerRow; i++)
      g.drawLine(x+i*w/optionsPerRow, y, x+i*w/optionsPerRow, y+lastLine-1);
    // vertical last row
    for (int i=1; i<lastRowOptions; i++)
      g.drawLine(x+i*w/optionsPerRow, y+lastLine, x+i*w/optionsPerRow, y+h-1);
    
    // icons
    g.setFont(font);
    final int fh = font.getHeight();
    final int selMode = paintArgs.getSelectionMode();
    for (int i=0; i<numOptions; i++) {
      final boolean isSelected = (i == current);
      
      final int row = i/optionsPerRow;
      final int col = i%optionsPerRow;
      final int xx = x+col*w/optionsPerRow + (col == 0 ? 0 : 1);
      final int yy = y+row*h/optionRows + (row == 0 ? 0 : 1);
      // final int ww = ((i == numOptions-1) ? (w-col*w/optionsPerRow) : ((col+1)*w/optionsPerRow-col*w/optionsPerRow)) - (col == 0 ? 0 : 1);
      int ww = (col+1)*w/optionsPerRow-col*w/optionsPerRow - (col == 0 ? 0 : 1);
      if (i == numOptions-1)
        ww = (w-col*w/optionsPerRow) - (col == 0 ? 0 : 1); 
      final int hh = (row+1)*h/optionRows-row*h/optionRows - (row == 0 ? 0 : 1);

      // Logger.debug("Welcome.paint #" + i + " @ " + xx + "," + yy + " " + ww + "x" + hh);
      // Logger.debug("Welcome.paint #" + i + " @ w=" + w + ", w2=" + ((col+1)*w/optionsPerRow));

      // paint selected background
      if (isSelected && selMode == WelcomePaintArgs.SELMODE_BG) {
        g.setColor(paintArgs.getSelectedColor());
        g.fillRect(xx, yy, ww, hh);
      }
      
      // wrap text
      final String[] wrappedText = StringTools.wrapText(options[i], font,
          ww-3-SPACING_SELECTBORDERS*2, 0);
      // final int textWidth = MGMTools.maxLength(wrappedText, font);
      final int textHeight = fh * wrappedText.length;
      final int imageWidth = icons[i].getWidth();
      final int imageHeight = icons[i].getHeight();
      final int totalHeight = imageHeight+textHeight+SPACING_IMGTEXT;
      
      // paint image and text
      final int yyy = (hh-totalHeight)/2;
      g.drawImage(icons[i], xx+(ww-imageWidth)/2, yy+yyy, Graphics.TOP|Graphics.LEFT);
      g.setColor(paintArgs.getTextColor());
      final int yt = yy+yyy+imageHeight+SPACING_IMGTEXT;
      for (int j=0; j<wrappedText.length; j++) {
        final int www = font.stringWidth(wrappedText[j]);
        g.drawString(wrappedText[j], xx+(ww-www)/2, yt+j*fh, Graphics.TOP|Graphics.LEFT);
      }
      
      // paint selection box
      if (isSelected && selMode == WelcomePaintArgs.SELMODE_RECT) {
        g.setColor(paintArgs.getSelectedColor());
        for (int j=0; j<SPACING_SELECTBORDERS; j++)
          g.drawRect(xx+j, yy+j, ww-1-2*j, hh-1-2*j);
      }
    }
  }
  
  /**
   * Left arrow key press.
   */
  public void keyLeft() {
    current = (current+numOptions-1) % numOptions;
  }
  
  /**
   * Right arrow key press.
   */
  public void keyRight() {
    current = (current+1) % numOptions;
  }
  
  /**
   * Down arrow key press.
   */
  public void keyDown() {
    int row = current/optionsPerRow;
    int col = current%optionsPerRow;
    if (row == optionRows-1) {
      row = 0;
      col = (col+1)%optionsPerRow;
    }
    else
      row++;
    current = row*optionsPerRow + col;
    if (current >= numOptions)
      current = numOptions-1;
  }
  
  /**
   * Up arrow key press.
   */
  public void keyUp() {
    int row = current/optionsPerRow;
    int col = current%optionsPerRow;
    if (row == 0) {
      row = optionRows-1;
      col = (col+optionsPerRow-1)%optionsPerRow;
    }
    else
      row--;
    current = row*optionsPerRow + col;
    if (current >= numOptions)
      current = numOptions-1;
  }
  
  /**
   * Select and return the option clicked.
   * @param x x coordinate for the click (relative to welcome screen top-left)
   * @param y y coordinate
   * @return the index of the option clicked
   */
  public int getClickedOption(int x, int y) {
    int row = -1;
    int col = -1;
    
    // clicked to the left or above the processed area
    if (x < 0 || y < 0)
      return -1;
    
    for (int i=0; i<optionsPerRow; i++)
      if ((i+1)*w/optionsPerRow > x) {
        col = i;
        break;
      }
    for (int i=0; i<optionRows; i++)
      if ((i+1)*h/optionRows > y) {
        row = i;
        break;
      }
    
    // clicked below or to the right of the processed area
    if (row < 0 || col < 0)
      return -1;
    
    // select option
    current = row*optionsPerRow+col;
    if (current >= numOptions)
      current = numOptions-1;
    
    // return
    return current;
  }
  
  /**
   * Get number of options.
   * @return number of options
   */
  public int getNumOptions() {
    return options.length;
  }
  
  /**
   * Get the current selected option.
   * @return current
   */
  public int getSelectedIndex() {
    return current;
  }

  private boolean needsSize(int newW, int newH) {
    return w == 0 || h == 0 || w != newW || h != newH;
  }
}
